

from src.common import *;
from src.social.model import *;
from src.social.spi import *;
from src.social.service import *;
from src.social.converters import *;
from src.social.sample import *;

class RestException(Exception):
	pass;

# Internal error code representations, these get translated into http codes in the outputError() function

# FIXME Delete should respond with a 204 No Content to indicate success
class RestServlet(HttpServlet):	
	#  The json Batch Route is used by the gadgets 
	__JSON_BATCH_ROUTE_ = "jsonBatch";

	def doGet(self):
		self.doPost('GET');

	def doPut(self):
		self.doPost('PUT');

	def doDelete(self):
		self.doPost('DELETE');

	def doPost(self, method = 'POST'):
		try:
			self.setNoCache(True);
			#  if oauth, create a token from it's values instead of one based on _get['st']/_post['st']
			#  NOTE : if no token is provided an anonymous one is created (owner = viewer = appId = modId = 0)
			#  keep this in mind when creating your data services.. 
			token = self.getSecurityToken();
			outputFormat = self.getOutputFormat();
			if (outputFormat == 'json'):
			
				self.setContentType('application/json');
				outputConverter = OutputJsonConverter();
				
			elif (outputFormat == 'atom'):
				self.setContentType('application/atom+xml');
				outputConverter = OutputAtomConverter();
				
			elif (outputFormat == 'xml'):
				self.setContentType('application/xml');
				outputConverter = OutputXmlConverter();
				
			else:
				self.outputError(ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
					
			if (self.isJsonBatchUrl()):
				#  custom json batch format used by the gadgets
				responses = self.handleJsonBatchRequest(token);
				outputConverter.outputJsonBatch(responses, token);
			else:
				#  single rest request
				response = self.handleRequest(token, method);
				outputConverter.outputResponse(response['response'], response['request']);
			
		except Exception(e):
			Util.header("HTTP/1.0 500 Internal Server Error");
			Util.write("<html><body><h1>500 Internal Server Error</h1>");
			Util.write("Message: " + e.value + "<br />\n");

			from lib import config;
			if (Config.get('debug')):
				Util.write("<pre>\n");
				Util.write(e);
				Util.write("\n</pre>");
			
			Util.write("</body></html>");
		
	def __handleRequest_(self, token, method):
		params = self.getListParams();
		requestItem = RestRequestItem();
		url = self.getUrl();
		requestType = self.getRouteFromParameter(url);
		requestFormat = self.getRequestFormat();
		requestParam = self.getRequestParams(requestType, requestFormat);
		requestItem.createRequestItem(url, token, method, params, requestParam);
		responseItem = self.getResponseItem(requestItem);
		return {'request' : requestItem, 'response' : responseItem};
	

	def __handleJsonBatchRequest_(self, token):
		#  we support both a raw http post (without application/x-www-form-urlencoded headers) like java does
		#  and a more php / curl safe version of a form post with 'request' as the post field that holds the request json data
		#[todo]
		#if (isset(GLOBALS['HTTP_RAW_POST_DATA']) or isset(_POST['request'])) {
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	

		if (requestUtilHandler.haeders.has_key('HTTP_RAW_POST_DATA') or requestUtilHandler.GETORPOST.has_key('request')):
			requests = self.getRequestParams('jsonbatch');
			responses = {};
			keys = requests.keys();
			for key in keys:
				value = requests[key];
				requestItem = RestRequestItem();
				requestItem.createRequestItemWithRequest(value, token);
				responses[key] = self.getResponseItem(requestItem);
			
			return responses;
		else:
			raise Exception, "No post data set";
		
	def __getResponseItem_(self,  requestItem):
		path = self.getRouteFromParameter(requestItem.getUrl());
		
		class1 = False;

		
		if (path == 'people'):
			class1 = PeopleHandler;
				
		elif (path == 'activities'):
			class1 = ActivitiesHandler;
			
		elif (path == 'appdata'):
			class1 = AppDataHandler;
			
		elif (path == 'messages'):
			class1 = MessagesHandler;
			
		# TODO add 'groups' and 'messages' here
		else:
			response = ResponseItem(NOT_IMPLEMENTED, "{path} is not implemented");
			
		#if (class and class_exists(class, True)) 
		if (class1):
			classhandler = class1(None);
			response = classhandler.handleMethod(requestItem);
		
		if (response.getError() != None and not self.isJsonBatchUrl()):
			#  Can't use http error codes in batch mode, instead we return the error code in the response item
			self.outputError(response);
		
		return response;
	

	def __decodeRequests_(self, requestParam, requestType, format = 'json'):
		if (len(requestParam) == 0):
			return None;
		
		if (format == 'json'):
			inputConverter = InputJsonConverter();
			
		elif (format == 'atom'):
			inputConverter = InputAtomConverter();
			
		elif (format == 'xml'):
			inputConverter = InputXmlConverter();
			
		else:
			raise Exception, "Invalid or unsupported input format";
			
		
		if (requestType == 'people'):
			ret = inputConverter.convertPeople(requestParam);
			
		elif (requestType == 'activities'):
			ret = inputConverter.convertActivities(requestParam);
			
		elif (requestType == 'messages'):
			ret = inputConverter.convertMessages(requestParam);
			
		elif (requestType == 'appdata'):
			ret = inputConverter.convertAppData(requestParam);
			
		elif (requestType == 'jsonbatch'):
			#  this type is only used by the internal json batch format
			if (format != 'json'):
				raise Exception, "the json batch only supports the json input format";
			
			ret = inputConverter.convertJsonBatch(requestParam);
			
		# TODO add 'groups' and 'messages' here
		else:
			raise Exception, "Unsupported REST call";
			
	
		return ret;
	

	def __getRequestParams_(self, requestType, requestFormat = 'json'):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		post = requestUtilHandler.GETORPOST['request'] if requestUtilHandler.GETORPOST.has_key('request') else None;
		requestParam = requestUtilHandler.headers['HTTP_RAW_POST_DATA'] if requestUtilHandler.headers.has_key('HTTP_RAW_POST_DATA') else post;

		#requestParam = isset(GLOBALS['HTTP_RAW_POST_DATA']) ? GLOBALS['HTTP_RAW_POST_DATA'] : post;

		#if (get_magic_quotes_gpc()) 
		#	requestParam = stripslashes(requestParam);
		
		return self.decodeRequests(requestParam, requestType, requestFormat);
	

	def __getRouteFromParameter_(self, pathInfo):
		from lib import Util;
		pathInfo = Util.substr(pathInfo, 1);
		indexOfNextPathSeparator = pathInfo.find("/");
		return Util.substr(pathInfo, 0, indexOfNextPathSeparator) if indexOfNextPathSeparator != -1 else pathInfo;
	

	def __outputError_(self,  response):
		errorMessage = response.getErrorMessage();

		if (response.getError() == BAD_REQUEST):
			code = '400 Bad Request';
			
		elif (response.getError() ==UNAUTHORIZED):
			code = '401 Unauthorized';
			
		elif (response.getError() ==FORBIDDEN):
			code = '403 Forbidden';
			
		elif (response.getError() ==NOT_FOUND):
			code = '404 Not Found';
			
		elif (response.getError() ==NOT_IMPLEMENTED):
			code = '501 Not Implemented';
			
		#elif (response.getError() ==INTERNAL_ERROR:
		else:
			code = '500 Internal Server Error';
			
		
		from lib import Util;
		Util.header("HTTP/1.0 " + code, True);
		Util.write( "code - errorMessage");
		Util.die();

	def __getSecurityToken_(self):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		token = requestUtilHandler.GETORPOST['st'] if requestUtilHandler.GETORPOST.has_key('st') else '';
		if (len(token) == 0):
			#  no security token, continue anonymously, remeber to check
			#  for private profiles etc in your code so their not publicly
			#  accessable to anoymous usersnot Anonymous == owner = viewer = appId = modId = 0
			from lib import config;
			gadgetSigner = Config.get('security_token');
			#  create token with 0 values, no gadget url, no domain and 0 duration
			return gadgetSigner(None, 0, 0, 0, 0, '', '', 0);

		if (len(token.splie(':')) != 6):
			import  urllib;
			token = urllib.quote(token.encode('base64'));
		
		gadgetSigner = Config.get('security_token_signer');
		gadgetSigner = gadgetSigner();
		return gadgetSigner.createToken(token);
	

	def __getOutputFormat_(self):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		output = requestUtilHandler.GETORPOST['format'] if len(requestUtilHandler.GETORPOST['format']) > 0 else 'json';
		return strtolower(trim(output));
	

	def __getListParams_(self):
		#  get the rest params
		from lib import Util;
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		uri = Util.substr(requestUtilHandler.headers["REQUEST_URI"], len(Config.get('web_prefix') + '/social/rest') + 1);
		pos = uri.find('?');
		if (pos != -1):
			uri = Util.substr(uri, 0, pos);
		
		restParams = uri.split('/');
		return restParams;
	

	def __getRequestFormat_(self):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		if (not requestUtilHandler.headers.has_key('CONTENT_TYPE')):
			#  if no Content-Type header is set, we assume json
			return 'json';
			
		if (requestUtilHandler.headers['CONTENT_TYPE'] == 'application/atom+xml'):
			return 'atom';
		elif (requestUtilHandler.headers['CONTENT_TYPE'] == 'application/json'):
			return 'json';
		else:
			raise Exception, "Invalid request content type";

	def __getUrl_(self):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		return Util.substr(requestUtilHandler.headers["REQUEST_URI"], len(Config.get('web_prefix') + '/social/rest'));
	

	def isJsonBatchUrl(self):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	
		return (RestServlet.JSON_BATCH_ROUTE.find(requestUtilHandler.headers["REQUEST_URI"]) != -1);
	
